package com.changgou.oauth.service.impl;

import com.changgou.oauth.service.AuthService;
import com.changgou.oauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: 大佬味的小男孩
 * @date: 2020-07-30 10:37
 **/
@Service
public class AuthServiceImpl implements AuthService {


    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${auth.ttl}")
    private Long ttl;
    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {

        //发送请求，申请令牌

        //1.1 采用客户端负载均衡，从eureka获取认证服务的ip和端口
        ServiceInstance serviceInstance = loadBalancerClient.choose("user-auth");
        //1.2 http://localhost:9200
        URI uri = serviceInstance.getUri();
        //1.3 http://localhost:9200/oauth/token
        String url = "http://localhost:9200/oauth/token";

        // 2. 封装请求参数 body , headers
        //2.1设置body 指定认证类型、账号、密码
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);
        //2.2 设置headers包括了httpbasic认证信息
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        //2.3 进行Base64编码,并将编码后的认证数据放到头文件中
        headers.add("Authorization", this.getHttpBasic(clientId, clientSecret));
        //2.5 创建requestEntity
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);

        //3. 当后端出现了401,400.后端不对着两个异常编码进行处理,而是直接返回给前端
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {
                    super.handleError(response);
                }
            }
        });

        //4. 发送请求：请求路径 请求方式 请求数据，数据返回的结果类型
        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, Map.class);

        Map map = responseEntity.getBody();
        if (map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {
            //获取令牌失败

        }
        System.out.println(map);


        //封装结果数据
        AuthToken authToken = new AuthToken();
        //强转成String类型
        authToken.setAccessToken((String) map.get("access_token"));
        authToken.setRefreshToken((String) map.get("refresh_token"));
        authToken.setJti((String) map.get("jti"));


        // 将jti作为redis的key,将jwt作为redis的value
        stringRedisTemplate.boundValueOps(authToken.getJti()).set(authToken.getAccessToken(),ttl, TimeUnit.SECONDS);

        return authToken;
    }

    //2.4 Base64编码
    private String getHttpBasic(String clientId, String clientSecret) {
        String value = clientId + ":" + clientSecret;
        byte[] encode = Base64Utils.encode(value.getBytes());
        //Basic Y2hhbmdnb3U6Y2hhbmdnb3U=
        return "Basic " + new String(encode);
    }
}
